package com.chushouya.manager.service.api.impl;

import com.chushouya.common.applet.*;
import com.chushouya.common.util.PasswordUtil;
import com.chushouya.manager.constants.*;
import com.chushouya.manager.dto.admin.user.login.*;
import com.general.framework.core.enums.StatusEnum;
import com.general.framework.core.lang.*;
import com.chushouya.common.constant.PlatformTypeEnum;
import com.general.framework.core.enums.ContextUserType;
import com.chushouya.common.constant.RedisKeyEnum;
import com.chushouya.common.support.Contexts;
import com.chushouya.common.support.RequestHeaderContext;
//=====manager.constants.*;
import com.chushouya.manager.constants.*;
import com.chushouya.manager.dao.entity.ClerkEntity;
import com.chushouya.manager.dao.entity.ProgramEntity;
import com.chushouya.manager.dao.repository.ClerkRepository;
import com.chushouya.manager.dao.repository.ProgramRepository;
import com.chushouya.manager.dao.repository.UserExtendRepository;
import com.chushouya.manager.dto.admin.clerk.ClerkQuery;
//=====manager.dto.admin.user.login.*;
//=====manager.service.api.*;
import com.chushouya.manager.dao.entity.UserEntity;
import com.chushouya.manager.dao.entity.UserExtendEntity;
import com.chushouya.manager.dto.admin.log.LoginLogDTO;
import com.chushouya.manager.dto.admin.login.WebAppLoginUser;
import com.chushouya.manager.dto.admin.sms.SmsDTO;
import com.chushouya.manager.dto.admin.user.login.*;
import com.chushouya.manager.service.admin.LoginLogService;
import com.chushouya.manager.service.api.UserApiService;
import com.chushouya.manager.service.api.UserExtendService;
import com.chushouya.manager.service.api.UserLoginService;
import com.chushouya.manager.service.api.WebAppTokenService;
import com.chushouya.manager.service.common.AlipayClientFactory;
import com.chushouya.manager.service.common.SmsService;
import com.general.framework.core.constant.Constants;
import com.general.framework.core.enums.BusinessStatus;
import com.general.framework.core.exception.Ex;
import com.general.framework.data.redis.RedisCache;
import com.general.framework.web.utils.IpAddressUtil;
import com.general.framework.web.utils.IpUtils;
import com.general.framework.web.utils.ServletUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
@RefreshScope
@Slf4j
public class UserLoginServiceImpl implements UserLoginService {


    @Resource
    private RedisCache redisCache;

    @Resource
    private UserApiService userApiService;

    @Resource
    private UserExtendService userExtendService;

    @Resource
    private WebAppTokenService webAppTokenService;

    @Resource
    private LoginLogService loginLogService;

    @Resource
    private ClerkRepository clerkRepository;

    @Resource
    private SmsService smsService;

    @Resource
    private UserExtendRepository userExtendRepository;

    @Resource
    private ProgramRepository programRepository;

    /**
     * 获取小程序信息
     */
    private ProgramEntity selectProgram(String appId) {
        ProgramEntity program = programRepository.selectByAppId(appId);
        if (Objects.isNull(program)) {
            throw Ex.business("小程序信息不存在");
        }
        return program;
    }

    @Override
    public void sendCode(String scene, String phone) {
        String code = IdTakes.takeRandomNum(4);
        Map<String, String> templateParams = new HashMap<>();
        templateParams.put("code", code);
        SmsDTO dto = new SmsDTO();
        dto.setTemplateCode(SmsTemplateEnum.APP_LOGIN_CODE.getCode());
        dto.setParams(templateParams);
        dto.setPhone(phone);
        log.info("发送短信验证码 scene:{}, phone:{}, code:{}", scene, phone, code);
        boolean result = smsService.sendDynamicTemplateSms(dto);
        if (result) {
            final String cacheKey = RedisKeyEnum.SMS_VALIDATE_CODE.value(scene, phone);
            redisCache.setCacheObject(cacheKey, code, 5L, TimeUnit.MINUTES);
        } else {
            throw Ex.business("发送验证码失败");
        }
    }

    @Override
    @Transactional
    public UserInfoDTO smsLogin(SmsLoginDTO dto) {
        final String phone = dto.getPhone();
        final String scene = SmsSceneEnum.H5_LOGIN.value();
        final String appId = dto.getAppId();
        final String platform = dto.getPlatform();
        smsService.validateSmsCode(scene, phone, dto.getCode());
        return this.handleMobileLogin(AppLoginRoleEnum.user, phone, phone, appId, platform);
    }


    @Override
    public String code2Session(Code2SessionDTO dto) {

        final String appid = dto.getAppId();
        final String platform = dto.getPlatform();
        final String code = dto.getCode();
        String userId;
        if (PlatformTypeEnum.ALIPAY.value().equals(platform)) {
            //支付宝小程序
            AlipayClient client = AlipayClientFactory.getInstance(appid);
            userId = client.jscode2session(code);

        } else if (PlatformTypeEnum.WEIXIN.value().equals(platform)) {
            //微信小程序
            ProgramEntity program = selectProgram(appid);
            WxClient client = new WxClient(appid, program.getSecret());
            userId = client.jscode2session(code);

        }  else if (PlatformTypeEnum.XHS.value().equals(platform)) {
            //小红书小程序
            ProgramEntity program = selectProgram(appid);
            XiaoHongShuClient client = new XiaoHongShuClient(appid, program.getSecret());
            userId = client.login(code);

        }  else if (PlatformTypeEnum.DOUYIN.value().equals(platform)) {
            //字节小程序
            ProgramEntity program = selectProgram(appid);
            ByteDanceClient client = new ByteDanceClient(appid, program.getSecret());
            userId = client.jscode2session(code);

        }  else {
            //platform不正确
            throw Ex.business("platform不正确");
        }

        if (Strings.isBlank(userId)) {
            throw Ex.business("获取openid异常");
        }

        return userId;
    }

    @Override
    @Transactional
    public UserInfoDTO appletLogin(AppletLoginDTO dto) {
        final String platform = dto.getPlatform();
        final String appId = dto.getAppId();
        final String openId = dto.getOpenId();
        final String phone = this.getAppletUserPhone(dto);
        final AppLoginRoleEnum loginRole = AppLoginRoleEnum.user;
        return this.handleMobileLogin(loginRole, openId, phone, appId, platform);
    }

    @Override
    public UserInfoDTO clerkLogin(ClerkLoginDTO dto) {
        final String appid = dto.getAppId();
        final String platform = PlatformTypeEnum.WEIXIN.value();
        AppletLoginDTO appletLoginDTO = Beans.copy(dto, AppletLoginDTO.class);
        appletLoginDTO.setPlatform(platform);
        final String phone = this.getAppletUserPhone(appletLoginDTO);
        final AppLoginRoleEnum loginRole = AppLoginRoleEnum.get(dto.getLoginRole());
        return this.handleMobileLogin(loginRole, phone, phone, appid, platform);
    }

    @Override
    public UserInfoDTO clerkSmsLogin(SmsLoginDTO dto) {
        final String phone = dto.getPhone();
        final String appId = dto.getAppId();
        final String platform = PlatformTypeEnum.WEIXIN.value();
        return this.handleMobileLogin(AppLoginRoleEnum.clerk, phone, phone, appId, platform);
    }

    @Override
    public String getAppletUserPhone(AppletLoginDTO dto) {

        final String platform = dto.getPlatform();
        final String appId = dto.getAppId();
        final String code = dto.getCode();
        final String encryptedData = dto.getEncryptedData();

        String phone;
        if (PlatformTypeEnum.ALIPAY.value().equals(platform)) {
            AlipayClient client = AlipayClientFactory.getInstance(appId);
            phone = client.getPhoneNumber(code);
        } else if (PlatformTypeEnum.WEIXIN.value().equals(platform)) {
            //微信小程序
            ProgramEntity program = selectProgram(appId);
            WxClient client = new WxClient(appId, program.getSecret());
            phone = client.getPhoneNumber(dto.getCode());
        }  else if (PlatformTypeEnum.XHS.value().equals(platform)) {
            ProgramEntity program = selectProgram(appId);
            XiaoHongShuClient client = new XiaoHongShuClient(appId, program.getSecret());
            phone = client.getPhoneNumber(encryptedData, dto.getOpenId(), dto.getIv());
        }  else if (PlatformTypeEnum.DOUYIN.value().equals(platform)) {
            ProgramEntity program = selectProgram(appId);
            WxClient client = new WxClient(appId, program.getSecret());
            phone = client.getPhoneNumber(dto.getCode());
        } else {
            throw Ex.business("未定义的平台类型");
        }

        if (Strings.isBlank(phone)) {
            throw Ex.business("获取手机号码异常");
        }
        return phone;
    }

    @Override
    public void logout(String token) {
        webAppTokenService.delLoginToken(token);
    }

    @Override
    public UserInfoDTO jPushLogin(JPushLoginDTO dto) {
        final String platform = dto.getPlatform();
        final String appId = dto.getAppId();
        String phone = JPushClient.loginTokenVerify(dto.getLoginToken());
        final AppLoginRoleEnum loginRole = AppLoginRoleEnum.user;
        return this.handleMobileLogin(loginRole,phone,phone,appId,platform);
    }

    @Override
    public void code2AlipayOpenId(Code2SessionDTO dto) {
        final String appId = dto.getAppId();
        final String platform = dto.getPlatform();
        final String code = dto.getCode();
        Long userId = Contexts.getUserContext().getUserId();
        AlipayClient client = AlipayClientFactory.getInstance(appId);
        String openId = client.jscode2session(code);
        Validate.isBlank(openId, "获取openId异常");
        userExtendService.saveUserExtend(userId, appId, openId, platform);
    }

    private void updateLoginInfo(UserEntity userEntity) {
        UserEntity updateEntity = new UserEntity();
        updateEntity.setUserId(userEntity.getUserId());
        updateEntity.setLastLoginTime(new Date());
        updateEntity.setLastLoginIp(IpUtils.getIpAddr(ServletUtils.getRequest()));
        userApiService.update(updateEntity);
    }
    @Override
    public UserInfoDTO handleMobileLogin(AppLoginRoleEnum loginRole, String openId, String phone, String appId, String platform) {
        Integer userType = UserTypeEnum.normal_user.value();
        WebAppLoginUser webAppLoginUser = new WebAppLoginUser();
        webAppLoginUser.setClientType(platform);
        switch (loginRole) {
            case clerk:
                validateClerkInfo(webAppLoginUser,phone);
                userType = UserTypeEnum.clerk.value();
                break;
            default:
                break;
        }
        UserEntity userEntity;
        UserExtendEntity userExtend = userExtendRepository.selectByAppIdAndOpenId(appId,openId);
        Date timeNow = Dates.getTimeNow();
        if (Objects.isNull(userExtend)) {
            userEntity = userApiService.selectByUserName(phone);
            if (Objects.isNull(userEntity)) {
                userEntity = userApiService.register(userType, phone, phone);
            }
            userExtend = new UserExtendEntity();
            userExtend.setUserId(userEntity.getUserId());
            userExtend.setOpenId(openId);
            userExtend.setPlatform(platform);
            userExtend.setAppId(appId);
            userExtend.setCreateTime(timeNow);
            userExtend.setUpdateTime(timeNow);
            userExtendRepository.insertSelective(userExtend);
        } else {
            userEntity = userApiService.selectByUserId(userExtend.getUserId());
        }
        //更新登录信息
        updateLoginInfo(userEntity);

        webAppLoginUser.setUserId(userEntity.getUserId());
        webAppLoginUser.setUserName(userEntity.getUserName());
        webAppLoginUser.setUserType(userType);
        webAppLoginUser.setPhone(userEntity.getUserPhone());

        String token = webAppTokenService.createToken(webAppLoginUser);
        UserInfoDTO userInfo = new UserInfoDTO();
        userInfo.setToken(token);
        userInfo.setUserId(webAppLoginUser.getUserId());
        userInfo.setPhone(webAppLoginUser.getPhone());
        userInfo.setUserType(webAppLoginUser.getUserType());
        userInfo.setUserName(webAppLoginUser.getUserName());
        userInfo.setNickName(userExtend.getNickName());
        userInfo.setAvatar(userExtend.getAvatar());
        userInfo.setOpenId(openId);
        userInfo.setPlatform(platform);

        this.updateLoginInfo(userEntity);
        this.saveLoginLog(userEntity.getUserType(), userEntity.getUserName(), token, Constants.LOGIN_SUCCESS, Strings.format("登录成功({})", platform));
        return userInfo;
    }

    @Override
    public UserInfoDTO getLoginUserInfo(String appId, Long userId) {
        final UserEntity userEntity = userApiService.selectByUserId(userId);

        UserInfoDTO userInfo = new UserInfoDTO();
        userInfo.setToken(Contexts.getHeaderContext().getToken());
        userInfo.setUserId(userEntity.getUserId());
        userInfo.setPhone(userEntity.getUserPhone());
        userInfo.setUserType(userEntity.getUserType());
        userInfo.setUserName(userEntity.getUserName());

        final UserExtendEntity userExtendEntity = userExtendService.selectByAppIdAndUserId(appId, userId);
        userInfo.setNickName(userExtendEntity.getNickName());
        userInfo.setAvatar(userExtendEntity.getAvatar());
        userInfo.setOpenId(userExtendEntity.getOpenId());
        userInfo.setPlatform(userExtendEntity.getPlatform());
        return userInfo;
    }

    public void saveLoginLog(Integer userType, String userName, String token, String status, String message) {
        final RequestHeaderContext header = Contexts.getHeaderContext();
        LoginLogDTO loginLog = new LoginLogDTO();
        loginLog.setUserType(userType);
        loginLog.setUserName(userName);
        loginLog.setLoginIp(header.getIp());
        loginLog.setLoginLocation(IpAddressUtil.getAddress(header.getIp()));
        List<String> loginDetailList = Lists.newArrayList();
        if (Strings.isNotEmpty(header.getOsType())) {
            loginDetailList.add("osType:".concat(header.getOsType()));
        }
        if (Strings.isNotEmpty(header.getOsVersion())) {
            loginDetailList.add("osVersion:".concat(header.getOsVersion()));
        }
        if (Strings.isNotEmpty(header.getPlatform())) {
            loginDetailList.add("platform:".concat(header.getPlatform()));
        }
        if (Strings.isNotEmpty(header.getAppVersion())) {
            loginDetailList.add("appVersion:".concat(header.getAppVersion()));
        }
        if (Strings.isNotEmpty(header.getDeviceId())) {
            loginDetailList.add("deviceId:".concat(header.getDeviceId()));
        }
        if (Strings.isNotEmpty(token)) {
            loginDetailList.add("token:".concat(token));
        }
        loginLog.setLoginDetail(Strings.join(loginDetailList, ","));
        if (Strings.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) {
            loginLog.setStatus(BusinessStatus.SUCCESS.getCode());
        } else if (Constants.LOGIN_FAIL.equals(status)) {
            loginLog.setStatus(BusinessStatus.FAIL.getCode());
        }
        loginLog.setMsg(message);
        loginLog.setLoginTime(Dates.getTimeNow());
        try {
            loginLogService.saveLoginLog(loginLog);
        } catch (Exception e) {
            log.error("保存登录日志失败");
        }
    }

    /**
     * 验证工程师登录信息
     */
    private void validateClerkInfo(WebAppLoginUser webAppLoginUser, String phone) {
        ClerkQuery query = new ClerkQuery();
        query.setClerkPhone(phone);
        final ClerkEntity clerkEntity = clerkRepository.selectOne(query);
        Check.notNull(clerkEntity, "手机号{}未注册业务员", phone);

        if (Objects.equals(clerkEntity.getStatus(), ClerkStatusEnum.auditing.value())) {
            this.loginLogService.saveLoginLog(ContextUserType.APP_CLERK, clerkEntity.getClerkName(), Constants.LOGIN_FAIL, Strings.format("业务员信息审核中({})", PlatformTypeEnum.WEIXIN.value()));
            throw Ex.business("工程师信息审核中，请联系平台");
        }
        if (Objects.equals(clerkEntity.getStatus(), ClerkStatusEnum.disable.value())) {
            this.loginLogService.saveLoginLog(ContextUserType.APP_CLERK, clerkEntity.getClerkName(), Constants.LOGIN_FAIL, Strings.format("业务员账号已停用({})", PlatformTypeEnum.WEIXIN.value()));
            throw Ex.business("登录失败，工程师账号已停用");
        }
        webAppLoginUser.setClerkId(clerkEntity.getClerkId());
    }

    @Override
    @Transactional
    public UserInfoDTO passwordLogin(PasswordLoginDTO dto) {
        final String phone = dto.getPhone();
        final String password = dto.getPassword();
        final String appId = dto.getAppId();
        final String platform = dto.getPlatform();

        // 根据手机号查询用户
        UserEntity userEntity = userApiService.selectByUserName(phone);
        if (Objects.isNull(userEntity)) {
            this.saveLoginLog(UserTypeEnum.normal_user.value(), phone, null, Constants.LOGIN_FAIL, "密码登录失败，用户不存在");
            throw Ex.business("手机号或密码错误");
        }

        // 验证密码
        if (Strings.isBlank(userEntity.getPassword())) {
            this.saveLoginLog(userEntity.getUserType(), phone, null, Constants.LOGIN_FAIL, "密码登录失败，未设置密码");
            throw Ex.business("该账号未设置密码，请使用验证码登录");
        }

        if (!PasswordUtil.matchesPassword(password, userEntity.getPassword())) {
            this.saveLoginLog(userEntity.getUserType(), phone, null, Constants.LOGIN_FAIL, "密码登录失败，密码错误");
            throw Ex.business("手机号或密码错误");
        }
        // 检查用户状态
        if (Objects.equals(userEntity.getStatus(), StatusEnum.disable.value())) {
            this.saveLoginLog(userEntity.getUserType(), phone, null, Constants.LOGIN_FAIL, "密码登录失败，账号已停用");
            throw Ex.business("账号已停用，请联系管理员");
        }

        // 创建登录会话
        WebAppLoginUser webAppLoginUser = new WebAppLoginUser();
        webAppLoginUser.setUserId(userEntity.getUserId());
        webAppLoginUser.setUserName(userEntity.getUserName());
        webAppLoginUser.setUserType(userEntity.getUserType());
        webAppLoginUser.setPhone(userEntity.getUserPhone());
        webAppLoginUser.setClientType(platform);

        String token = webAppTokenService.createToken(webAppLoginUser);

        // 获取用户扩展信息
        UserExtendEntity userExtend = userExtendRepository.selectByAppIdAndUserId(appId, userEntity.getUserId());
        
        UserInfoDTO userInfo = new UserInfoDTO();
        userInfo.setToken(token);
        userInfo.setUserId(userEntity.getUserId());
        userInfo.setPhone(userEntity.getUserPhone());
        userInfo.setUserType(userEntity.getUserType());
        userInfo.setUserName(userEntity.getUserName());
        
        if (Objects.nonNull(userExtend)) {
            userInfo.setNickName(userExtend.getNickName());
            userInfo.setAvatar(userExtend.getAvatar());
            userInfo.setOpenId(userExtend.getOpenId());
            userInfo.setPlatform(userExtend.getPlatform());
        }

        // 更新登录信息
        this.updateLoginInfo(userEntity);
        this.saveLoginLog(userEntity.getUserType(), userEntity.getUserName(), token, Constants.LOGIN_SUCCESS, Strings.format("密码登录成功({})", platform));
        
        return userInfo;
    }

}
